#include "StdAfx.h"
#include "WPFCaller.h"
#include "CWHPaccess.h"

using namespace Ooganizer;

// Specifies a nonzero timer identifier
#define NID_RESTORE_EVENT_TIMER (WM_USER + 1)

// * Messages send from WPFCaller class to WPFProc Thread
#define WM_KILLTHREAD (WM_USER + 501)

// * Messages Send from W32Button to WPFProc Thread
#define WM_GETBUTTONTHEMESETTING (WM_USER + 601)
#define WM_W32BUTTONCLICKED (WM_USER + 602)

// * External Messages * Send to us by outside
// Programs (most likely WPFForm)
#define WM_W32SETBUTTONSTATE (WM_USER + 701)

#ifdef _DEBUG
	#import "..\\..\\..\\Target\\Debug\\Platform\\Ooganizer.Platform.OoganizerSettings.tlb" raw_interfaces_only
#else
	#import "..\\..\\..\\Target\\Release\\Platform\\Ooganizer.Platform.OoganizerSettings.tlb" raw_interfaces_only
#endif
using namespace Ooganizer_Platform_OoganizerSettings;

// access the SharedDS *initialized by WPFCaller() constructor*
CWHPaccess* s_WPFCallerAccess = NULL;

//////////////////////////////////////////////////////////////////////
// Func: ReadButtonThemeSetting
// Desc: static function to quickly read in Button Theme Settings.
//	     ~Uses COM, must be called ONLY from within a COM initialized Thread!
//////////////////////////////////////////////////////////////////////
bool ReadButtonThemeSetting(WPFCaller* pCaller)
{
	HRESULT hr = S_FALSE;	

	_ButtonThemeNIconSettingsCCWPtr pButtonThemeNIconSetting;
	hr = pButtonThemeNIconSetting.CreateInstance(__uuidof(ButtonThemeNIconSettingsCCW));

	if(SUCCEEDED(hr))		
	{
		log(LOGGING_HIGH,"Succeed creating ButtonThemeNIconSettings Instance");
	}
	else
	{
		log(LOGGING_LOW,"FAILED to create the ButtonThemeNIconSettings COM Instance");
		return false;
	}
	////
	// Get the Button Theme N Icon Settings
	////
	IButtonThemeSettingPtr pThemeSetting;
	IButtonIconLocationPtr pIconLocation;
	pButtonThemeNIconSetting->GetIButtonThemeInterface(&pThemeSetting);
	pButtonThemeNIconSetting->GetIButtonIconLocation(&pIconLocation);	

	if(pThemeSetting == NULL)
		log(LOGGING_LOW,"pThemeSetting == NULL - Error");
	if(pIconLocation == NULL)
		log(LOGGING_LOW,"pIconLocation == NULL - Error");

	if((pThemeSetting == NULL) || (pIconLocation == NULL))
		return false;

	////
	// Get Button Dimension/Location
	////
	if(SUCCEEDED(hr))
		hr = pThemeSetting->Top(&pCaller->m_ButtonThemeSetting.Top);	
	if(SUCCEEDED(hr))
		hr = pThemeSetting->Right(&pCaller->m_ButtonThemeSetting.Right);
	if(SUCCEEDED(hr))
		hr = pThemeSetting->Height(&pCaller->m_ButtonThemeSetting.Height);
	if(SUCCEEDED(hr))
		hr = pThemeSetting->Width(&pCaller->m_ButtonThemeSetting.Width);
		
	////
	// Get Button Icon Location
	////
	if(SUCCEEDED(hr))
		pIconLocation->GetBUTTON_UP(pCaller->m_ButtonThemeSetting.bstrBUTTON_UP.GetAddress());
	if(SUCCEEDED(hr))
		pIconLocation->GetBUTTON_DOWN(pCaller->m_ButtonThemeSetting.bstrBUTTON_DOWN.GetAddress());
	if(SUCCEEDED(hr))
		pIconLocation->GetTOGGLE_UP(pCaller->m_ButtonThemeSetting.bstrTOGGLE_UP.GetAddress());
	
	if(SUCCEEDED(hr))
	{
		log(LOGGING_HIGH,"ReadButtonTheme Settings Successfully - Height=%i,Width=%i,Top=%i,Right=%i", 
			pCaller->m_ButtonThemeSetting.Height,pCaller->m_ButtonThemeSetting.Width,pCaller->m_ButtonThemeSetting.Top, pCaller->m_ButtonThemeSetting.Right);

		if(pCaller->m_ButtonThemeSetting.bIsInitialized)
			pCaller->m_ButtonThemeSetting.bIsChanged = true;

		if(!pCaller->m_ButtonThemeSetting.bIsInitialized)
			pCaller->m_ButtonThemeSetting.bIsInitialized = true;
	}
	else
	{
		pCaller->m_ButtonThemeSetting.bIsChanged = false;
		pCaller->m_ButtonThemeSetting.bIsInitialized = false;
		log(LOGGING_LOW,"ReadButtonTheme Settings FAILED!");
	}

	if(SUCCEEDED(hr))
		return true;
	else
		return false;
}

////
// For wndproc to know what we care about and pass it down to to W32WndProc
// ~these messages will be tagged with WM_USER for us to handle in our W32WndProc
////
UINT s_nMessagesWPFormCaresAbout = 2;
UINT s_MessagesWPFormCaresAbout[] =
{	
	WM_ACTIVATE,	
	WM_SIZE,	
};
bool g_IsMessageWPFormCaresAbout(UINT msgID)
{
	for(UINT i = 0; i < s_nMessagesWPFormCaresAbout; ++i)
	{
		if(s_MessagesWPFormCaresAbout[i] == msgID)
			return true;
	}
	return false;
}
////
// For wndproc to know what we care about and pass it down to to MainWndProc
// ~these messages will be send straight down for us to handle in our MainWndProc
////
UINT s_nMessagesW32WindowCaresAbout = 7;
UINT s_MessagesW32WindowCaresAbout[] =
{
	WM_PAINT,
	WM_DISPLAYCHANGE,
	WM_THEMECHANGED,
	WM_SIZE,
	WM_MOVE,
	WM_LBUTTONDOWN,
	WM_MOUSEMOVE,
};
bool g_IsMessageW32WindowCaresAbout(UINT msgID)
{
	for(UINT i = 0; i < s_nMessagesW32WindowCaresAbout; ++i)
	{
		if(s_MessagesW32WindowCaresAbout[i] == msgID)
			return true;
	}
	return false;
}
//////////////////////////////////////////////////////////////////////
// Desc: We need to make sure that we have the init button settings,
//	     before we can continue in many of our Messaging functions
//////////////////////////////////////////////////////////////////////
void MakeSureButtonThemeSettingWereInitialized(WPFCaller* pCaller)
{
	// We must make sure the Button has all the settings before
	// we can draw,move it, etc so sleep the thread until we know we have them
	while(!pCaller->m_ButtonThemeSetting.bIsInitialized)			
		Sleep(20);	
}
//////////////////////////////////////////////////////////////////////
// Desc: For our WPFThread callback into WPForm we must make sure
//		 that we have the handle for our W32Button so to pass it down to them
//////////////////////////////////////////////////////////////////////
void MakeSureW32ButtonWindowGotCreated(WPFCaller* pCaller)
{
	// We must make sure the Button window got created so we can pass the
	// handle down 
	while(pCaller->m_hwndW32Window == NULL)			
		Sleep(20);	
}
//////////////////////////////////////////////////////////////////////
// Desc: For our Win32 Thread we must make sure we have a valid pointer to
//	     a WPFCaller Object.
//////////////////////////////////////////////////////////////////////
WPFCaller* GetWPFCallerObject(HWND ParenthWnd)
{
	if(s_WPFCallerAccess)
	{
		BLANK_HookWndItem(Item);
		Item = s_WPFCallerAccess->GetHookedWndItem(ParenthWnd);
		WPFCaller* pCaller = Item.wpfcaller;
		return pCaller;
	}
	return NULL;
}
//////////////////////////////////////////////////////////////////////
// Func: DrawIconButton (Owner Drawing out Buttons)
// Desc: Responsible for drawing the owner buttons onto the caption bar
//////////////////////////////////////////////////////////////////////
bool DrawIconButton(HINSTANCE hInstance,HDC hdc, WPFCaller* pCaller) 
{
	// static Icon handles:	
	static HANDLE hCurrIcon = NULL;
	static HANDLE hButtonUp = NULL; 
	static HANDLE hButtonDown = NULL;
	static HANDLE hToggleUp = NULL;	
	
	// LoadIcon only loads once, but LoadImage does not, 
	// so in case you call the latter, use this flag
	static bool bIconsLoaded = false;

	// Make sure that we don't continue unless we have all the settings
	MakeSureButtonThemeSettingWereInitialized(pCaller);

	if (!bIconsLoaded || pCaller->m_ButtonThemeSetting.bIsChanged) 
	{
		////
		// ButtonUp
		////
		if(!hButtonUp)
		{
			HANDLE hTemp = hButtonUp; 
			hButtonUp = NULL;  //*avoid poss. threading issue
			DestroyIcon((HICON)hTemp);
		}
		hButtonUp = LoadImage(hInstance, (WCHAR*)pCaller->m_ButtonThemeSetting.bstrBUTTON_UP,IMAGE_ICON,pCaller->m_ButtonThemeSetting.Width,pCaller->m_ButtonThemeSetting.Height,LR_LOADFROMFILE);
		if(!hButtonUp)
		    log(LOGGING_LOW,"hButtonUp LoadImage Failed! with error %i - path %s",GetLastError(),(char*)pCaller->m_ButtonThemeSetting.bstrBUTTON_UP);

		////
		// ButtonDown
		////
		if(!hButtonDown)
		{
			HANDLE hTemp = hButtonDown; 
			hButtonDown = NULL; //*avoid poss. threading issue
			DestroyIcon((HICON)hTemp);
		}
		hButtonDown = LoadImage(hInstance, (WCHAR*)pCaller->m_ButtonThemeSetting.bstrBUTTON_DOWN,IMAGE_ICON,pCaller->m_ButtonThemeSetting.Width,pCaller->m_ButtonThemeSetting.Height,LR_LOADFROMFILE);
		if(!hButtonDown)
			log(LOGGING_LOW,"hButtonDown LoadImage Failed! with error %i - path %s",GetLastError(),(char*)pCaller->m_ButtonThemeSetting.bstrBUTTON_DOWN);				
		
		////
		// ToogleUp
		////
		if(!hToggleUp)
		{
			HANDLE hTemp = hToggleUp; 
			hToggleUp = NULL;  //*avoid poss. threading issue
			DestroyIcon((HICON)hTemp);
		}
		hToggleUp = LoadImage(hInstance, (WCHAR*)pCaller->m_ButtonThemeSetting.bstrTOGGLE_UP,IMAGE_ICON,pCaller->m_ButtonThemeSetting.Width,pCaller->m_ButtonThemeSetting.Height,LR_LOADFROMFILE);
		if(!hToggleUp)
			log(LOGGING_LOW,"hToggleUp LoadImage Failed! with error %i - path %s",GetLastError(),(char*)pCaller->m_ButtonThemeSetting.bstrTOGGLE_UP);
		
		// Everything went well.
		bIconsLoaded = true;
		pCaller->m_ButtonThemeSetting.bIsChanged = false;
	}	

	////
	// Now Draw the proper Icon State
	////
	bool bUseToogleState = (pCaller->m_ButtonToggledState != BUTTON_NONE); 

	if(!bUseToogleState && pCaller->m_ButtonState == BUTTON_DOWN)
		hCurrIcon = hButtonDown;	
	else if(pCaller->m_ButtonState == BUTTON_UP)				
		hCurrIcon = hButtonUp;	
	else if(bUseToogleState || pCaller->m_ButtonState == BUTTON_TOGGLED)	
		hCurrIcon = hToggleUp;

	if(hCurrIcon != NULL)
	{
		if (!DrawIconEx(hdc,0,0,(HICON) hCurrIcon,
			pCaller->m_ButtonThemeSetting.Width,
			pCaller->m_ButtonThemeSetting.Height,
			0, NULL, DI_NORMAL)) 
		{
			log(LOGGING_HIGH,"DrawIconEx Failed!");
			return false;
		}
	}

	return true;
}
//////////////////////////////////////////////////////////////////////
// Desc: Set static HeightNWidth (keeping track of the Window size)
//////////////////////////////////////////////////////////////////////
void Helper_GetRectAndSetHeightNWidth(HWND hWnd, WPFCaller* pCaller)
{
	RECT rect;
	GetWindowRect(hWnd,&rect);

	pCaller->m_wndHeight = (rect.bottom - rect.top);
	pCaller->m_wndWidth = (rect.right - rect.left);
	pCaller->m_wndRECT = rect;
}
//////////////////////////////////////////////////////////////////////
// Desc: Set static HeightNWidth (keeping track of the Window size)
//////////////////////////////////////////////////////////////////////
void Helper_GetRectAndSetHeightNWidthUsingPlacement(HWND hWnd, WPFCaller* pCaller)
{
	WINDOWPLACEMENT place;
	place.length = sizeof(WINDOWPLACEMENT);
	GetWindowPlacement(hWnd,&place);

	pCaller->m_wndHeight = (place.rcNormalPosition.bottom - place.rcNormalPosition.top);
	pCaller->m_wndWidth = (place.rcNormalPosition.right - place.rcNormalPosition.left);
	pCaller->m_wndRECT = place.rcNormalPosition;
}
//////////////////////////////////////////////////////////////////////
// Func: W32WndProc (W32Window)
// Desc: Responsible for handling the Wnd32Window's incoming messages
//////////////////////////////////////////////////////////////////////
LRESULT CALLBACK W32WndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
	LRESULT result = 0;
	PAINTSTRUCT ps;
	HDC hdc;
	int nTop = 0;
	int nRight = 0;
	bool bIsMaximized = false;
	HINSTANCE hInst = NULL;

	// Use the ParentHWND to get access to the correct WPFCaller instance,
	// we need to do this in case multiple windows exist in the same process
	WPFCaller* pCaller = GetWPFCallerObject(::GetParent(hWnd));
	
	// Nothing to do here, WPFCaller is not ready
	if(pCaller == NULL)
		goto HANDLE_MESSAGE;
	
	////
	// Messages should be handled here (if we are interested)
	////
	try
	{
		// For some calls we need th HINSTANCE
		hInst = pCaller->m_hInst;

		switch(Msg)
		{
			////
			// Win32Button Messages we handle
			////
			case WM_SIZE:
				if(wParam == SIZE_MINIMIZED)
				{
					pCaller->m_bButtonIsMinimized = true;
				}
				else if((wParam == SIZE_RESTORED && pCaller->m_bButtonIsMinimized) ||
					    (wParam == SIZE_MAXIMIZED && pCaller->m_bButtonIsMinimized))
				{
					// the Button receives this message way before the parent does.
					pCaller->m_bButtonIsBeingRestored = true;
				}
			case WM_DISPLAYCHANGE:
			case WM_MOVE:
				{
					// ~Calculate Offset for Maximized Windows - IMP
					// in vista, we need 7 height offset
					//			 we need 5 width offset
					// ~i tried using the best values that make sense
					const int YHEIGHT_OFFSET = GetSystemMetrics(SM_CYFRAME) - 1; // subtract 1 to better position the button (and not cut it off as much)
					const int XWIDTH_OFFSET = (2 * GetSystemMetrics(SM_CXEDGE)) + GetSystemMetrics(SM_CXBORDER);
					bool bIsMaximized = IsZoomed(pCaller->m_hWnd);

					int YOFFSET = 0;
					int XOFFSET = 0;
					if(bIsMaximized)
					{
						YOFFSET = YHEIGHT_OFFSET;
						XOFFSET = XWIDTH_OFFSET;
					}

					// Set the Window Position (Use Offset if need be)
					MakeSureButtonThemeSettingWereInitialized(pCaller);
					Helper_GetRectAndSetHeightNWidth(pCaller->m_hWnd, pCaller);
					nTop = pCaller->m_wndRECT.top + pCaller->m_ButtonThemeSetting.Top;
					nRight = pCaller->m_wndRECT.left + pCaller->m_wndWidth - pCaller->m_ButtonThemeSetting.Right;			
					SetWindowPos(hWnd, HWND_TOP, nRight - XOFFSET, nTop + YOFFSET, pCaller->m_ButtonThemeSetting.Width, pCaller->m_ButtonThemeSetting.Height, NULL);

					bool bIsMinimizeMoveMessage = false;
					if(Msg == WM_MOVE)
					{
						int x = LOWORD(lParam);
						int y = HIWORD(lParam);
						bIsMinimizeMoveMessage = (x > 33500) && (y > 33500);
					}
					// If the window is just too damn small to hold all the buttons just hide it!
					// ~better user experience
					const int MIN_WITDH_TO_DISPLAY_BUTTON_IN = 300;
					if((pCaller->m_wndWidth > 25) && /* make sure width is bigger >0 may as well put in some magic number buffer */ 
						!bIsMinimizeMoveMessage &&
					   (pCaller->m_wndWidth < MIN_WITDH_TO_DISPLAY_BUTTON_IN) && !pCaller->m_bButtonIsArtificiallyHidden && !pCaller->m_bButtonIsMinimized)
					{
						log(LOGGING_HIGH,"Setting Artificially Hidden to true");
						pCaller->m_bButtonIsArtificiallyHidden = true;
						RedrawWindow(pCaller->m_hwndW32Window,NULL,NULL,(RDW_INVALIDATE | RDW_UPDATENOW));
					}
					else if((pCaller->m_wndWidth >= MIN_WITDH_TO_DISPLAY_BUTTON_IN) && pCaller->m_bButtonIsArtificiallyHidden && !pCaller->m_bButtonIsMinimized)
					{
						log(LOGGING_HIGH,"Setting Artificially Hidden to false");
						pCaller->m_bButtonIsArtificiallyHidden = false;
						
						pCaller->StartRestoreEventFadeTimer();

						//RedrawWindow(pCaller->m_hwndW32Window,NULL,NULL,(RDW_INVALIDATE | RDW_UPDATENOW));
					}
					return 0;
					break;
				}
			case WM_MOUSEMOVE:
				TRACKMOUSEEVENT mouseEvent;
				mouseEvent.cbSize = sizeof(TRACKMOUSEEVENT);
				mouseEvent.dwFlags = TME_HOVER | TME_LEAVE;
				mouseEvent.hwndTrack = hWnd;
				mouseEvent.dwHoverTime = 25;
				TrackMouseEvent(&mouseEvent);
				break;
			case WM_LBUTTONDOWN:			
				log(LOGGING_HIGH,"Received ButtonClick Sending Click Event to WPForm");
				PostThreadMessage(pCaller->m_threadID,WM_W32BUTTONCLICKED,NULL,NULL);			
				return 0; 
			case WM_MOUSEHOVER:
				pCaller->m_ButtonState = BUTTON_UP;
				RedrawWindow(pCaller->m_hwndW32Window,NULL,NULL,(RDW_INVALIDATE | RDW_UPDATENOW));			
				return 0;
			case WM_MOUSELEAVE:
				pCaller->m_ButtonState = BUTTON_DOWN;
				RedrawWindow(pCaller->m_hwndW32Window,NULL,NULL,(RDW_INVALIDATE | RDW_UPDATENOW));
				return 0;
			case WM_TIMER:
				// Monitor Last Paint Message
				if(wParam == NID_RESTORE_EVENT_TIMER)
				{	
					//log(LOGGING_HIGH,"Received Restore_Event Timer Message");

					// Increment Opaqueness
					const int LOWER_INCREMENT = 7;
					const int MIDDLE_INCREMENT = 15;
					const int UPPER_INCREMENT = 25;
				
					// Increment Points
					const int LOWER_POINT = 60;
					const int MIDDLE_POINT = 160;
					const int UPPER_POINT = 230;
 
					BYTE alpha = pCaller->GetAlpha();
					if(alpha >= UPPER_POINT)
						alpha = 255; // just show the thing already
					else if(alpha <= LOWER_POINT)
						alpha += LOWER_INCREMENT;
					else if(alpha <= MIDDLE_POINT)
						alpha += MIDDLE_INCREMENT;
					else
						alpha += UPPER_INCREMENT;

					// Set the calculated Alpha Opaqueness
					pCaller->SetAlpha(alpha);

					// Close Restore Timer if we are done
					if(pCaller->GetAlpha() == 255)
					{
						// Kill the Restore Timer
						KillTimer(hWnd,NID_RESTORE_EVENT_TIMER);
						log(LOGGING_LOW,"Stopping Restore Event Timer");
					}
				}
				break;
			case WM_PAINT:
				// If we are minimized and we are getting messages from our parent
				// ~now we can launch the restore Timer
				if(pCaller->m_bButtonIsBeingRestored &&
				  (wParam == (WPARAM) pCaller->m_hWnd) && (lParam == (LPARAM) pCaller->m_hWnd))
				{
					// restore timer will from now on handle the restore
					pCaller->m_bButtonIsBeingRestored = false;
					pCaller->m_bButtonIsMinimized = false;

					// Hides the Button Completly
					pCaller->StartRestoreEventFadeTimer();
				}

				// Only Draw the button when we want to
				if(!pCaller->m_bButtonIsMinimized &&
				   !pCaller->m_bButtonIsArtificiallyHidden)
				{
					hdc = BeginPaint(hWnd, &ps);
					DrawIconButton(hInst,ps.hdc,pCaller);
					EndPaint(hWnd, &ps);
				}
				return 0;
			case WM_THEMECHANGED:						
				log(LOGGING_HIGH,"Reiceived WM_THEMECHANGED. W32Button is trying to retrieve new Theme Settings via WPFProc");			
				PostThreadMessage(pCaller->m_threadID,WM_GETBUTTONTHEMESETTING,NULL,NULL);
				break;
			case WM_W32SETBUTTONSTATE:
				log(LOGGING_HIGH, "Received WM_W32SetButtonState Message. Setting Button to %d", (int)lParam);
				pCaller->m_ButtonState = (BUTTON_STATE) lParam;

				// Button will be toggled / untoggled when the caller set it
				if(pCaller->m_ButtonState == BUTTON_TOGGLED)
					pCaller->m_ButtonToggledState = BUTTON_TOGGLED;
				else
					pCaller->m_ButtonToggledState = BUTTON_NONE;

				RedrawWindow(pCaller->m_hwndW32Window,NULL,NULL,(RDW_INVALIDATE | RDW_UPDATENOW));
				return 0;
		}
	}
	catch(...)
	{
		log(LOGGING_LOW, "*Error* - A fatal error occured in W32WndProc for W32Button");
	}

	HANDLE_MESSAGE:
	LRESULT lr = DefWindowProc(hWnd,Msg,wParam,lParam);
	return (lr);
}
//////////////////////////////////////////////////////////////////////
// Func: WPFThreadProc (WPFButtonForm)
// Desc: Responsible for handling messages that are supposed to go to the WPFButtonForm
//////////////////////////////////////////////////////////////////////
DWORD WINAPI WPFThreadProc(__in  LPVOID lpParameter)
{
	CoInitialize(NULL);
	MSG msg;

	// Force the system to create a message queue for the thread
	PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);

	IButtonFormPtr wpfForm;
	WPFCaller* pCaller = (WPFCaller*) lpParameter;

	// handle to parent window (Hooked Window)
	HWND hWnd = pCaller->m_hWnd;

	// First thing we want to do is read the icon setting for the win32 button
	// *we are doing it here because we are reading the settings using COM,
	// and this thread is COM enabled
	if(!ReadButtonThemeSetting(pCaller))
		return -1;
	
	// We need to sit here idly and wait to make sure that we got the Button Window Handle
	MakeSureW32ButtonWindowGotCreated(pCaller);
	HWND hWndButton = pCaller->m_hwndW32Window;  // handle to the Button Window
	log(LOGGING_HIGH, "WPFThreadProc: Wait over. ButtonW32 Window got created (%d).", hWndButton);

	// Try to create a WPF Form Objects
	HRESULT hr = wpfForm.CreateInstance(__uuidof(ButtonWPFormCCW));

	if(SUCCEEDED(hr))
	{	
		try
		{
			// Let the WPF form know that we activated (and pass out the window handle
			// that activated it) ~this is importanted for housekeeping. also pass out the
			// Button Handle so that it can set it's state. ~Imp
			log(LOGGING_MEDIUM, "Calling WPForm Activate Method with Parent (%d) and Button (%d)", hWnd, hWndButton);
			wpfForm->WPForm_Activated((long)hWnd, (long) hWndButton);
			log(LOGGING_MEDIUM, "Calling WPForm Activate Returned");

			// Let OnDefault know that this Thread is ready to receive messages		
			pCaller->m_bThreadIsReady = true;		
			BOOL bRet = FALSE;

			while( (bRet = GetMessage( &msg, (HWND)-1, 0, 0 )) != 0)
			{ 
				if (bRet == -1)
				{
					// handle the error and possibly exit
					log(LOGGING_LOW,"An Error Occured in ThreadProc. Exiting!", hr);
					goto STOPPING_THREAD;
					break;
				}
				else
				{
					////
					// MAIN MESSAGE LOOP
					////
					switch(msg.message)
					{				
						case WM_KILLTHREAD:						
							log(LOGGING_MEDIUM,"WM_KILLTHREAD was Posted to Thread. Exiting Window(%d) in pid(%d).", pCaller->m_hWnd, GetCurrentProcessId());
							wpfForm->Close();
							pCaller->m_hwndWPForm = NULL;
							pCaller->m_bThreadIsReady = false;
							goto STOPPING_THREAD;
							break;	
						case WM_GETBUTTONTHEMESETTING:
							if(ReadButtonThemeSetting(pCaller))
								log(LOGGING_HIGH,"Received GetButtonThemeSetting Message - Read ButtonThemeSettings Succeeded");
							else
								log(LOGGING_LOW,"Received GetButtonThemeSetting Message - Read ButtonThemeSettings Failed!");
							break;
						case WM_W32BUTTONCLICKED:
							log(LOGGING_MEDIUM,"WM_W32BUTTONCLICKED passing down to WPFForm");
							pCaller->m_hwndWPForm = (HWND) wpfForm->Open();
							break;
						case (WM_USER + WM_ACTIVATE):
							if((LOWORD(msg.wParam) == WA_ACTIVE) ||
								(LOWORD(msg.wParam) == WA_CLICKACTIVE))
							{							
								//if(HIWORD(msg.wParam)!=0)
								//	//wpfForm->RestoredFromMinimize();
								//else							  
							}
							else if(LOWORD(msg.wParam) == WA_INACTIVE)						
								///wpfForm->Event_Deactivated();						
							break;				
						case (WM_USER + WM_SIZE):												
							if(msg.wParam == SIZE_MAXIMIZED)												
								//wpfForm->Event_MaximizeOccured((long)HIWORD(msg.lParam),(long)LOWORD(msg.lParam));						
							break;
					}
				}
			}

			// Let the WPF Form know that this thread is exiting to allow it to do
			// any cleanup it may want to do
			STOPPING_THREAD:
			//wpfForm->WPForm_Deactivated();
			int IsThisTheEnd = 5;
		}
		catch(...)
		{
			log(LOGGING_LOW,"*Error* - A fatal Error Occured in WPFThreadProc WpfButton");
		}		
	}
	else
	{
		log(LOGGING_LOW,"Error occured creating WPForm Instance COM %i",hr);
	}

	CoUninitialize();
	return 0;
}	
//******************************************************************** 
// Class        WPFCaller	
//
// Programmer	Daniel Romischer
//              Ooganizer Development Team
//
// Description	~This class holds the default wndproc to process all the
//				 messages and send them to the WPF COM Object via the COM Thread.
//
// Note		   ~ All the host's Windows Messages enter in on OnDefault() 
//
// Created	   05-26-2008
//********************************************************************	
//////////////////////////////////////////////////////////////////////
//******************************************************************** 
// Construction / Destruction
//******************************************************************** 
//////////////////////////////////////////////////////////////////////
WPFCaller::WPFCaller(HookWndItem Item, HINSTANCE hInstance):
m_hWnd(Item.hWnd),
m_hwndWPForm(NULL),
m_hwndW32Window(NULL),
m_DefWndProc(Item.DefWndProc),
m_HookWndProc(Item.HookWndProc),
m_thread(NULL),
m_threadID(0),
m_bThreadIsReady(false),
m_hInst(hInstance),
m_wndHeight(0),
m_wndWidth(0),
m_ButtonState(BUTTON_NONE),
m_ButtonToggledState(BUTTON_NONE),
m_ButtonStateLastSaved(BUTTON_NONE),
m_ButtonToggledStateLastSaved(BUTTON_NONE),
m_bButtonIsArtificiallyHidden(false),
m_bButtonIsMinimized(false),
m_bButtonIsBeingRestored(false)
{
	// Allow us access to the static DS *only load once*
	if(s_WPFCallerAccess == NULL)
		s_WPFCallerAccess = new CWHPaccess();

	// just to make sure (to avoid any thread issues) assign out the this instance
	// BEFORE creating the threads *below*.
	Item.wpfcaller = this;
	if(s_WPFCallerAccess->SetHookedWndItem(Item.hWnd, Item) == FALSE)
		log(LOGGING_LOW, "WPFCaller SetHookedWndItem FAILED! == NOT GOOD");

	// Create the COM WPF Object for WPF Power and Flexibility
	m_thread = CreateThread(NULL,0,&WPFThreadProc,(LPVOID)this,0,&m_threadID);	
	log(LOGGING_HIGH, "WPFCaller Spawned WPFThreadID %i",m_threadID);

	// We are creating a new Thread to deal with 2 windows (WPForm Window,
	// Start Window32 Button (for speed)	
	Start();
}
WPFCaller::~WPFCaller()
{
	log(LOGGING_LOW, "~WPFCaller() got called");

	// Destroy the Button Window
	if(m_hwndW32Window && ::IsWindow(m_hwndW32Window))
		::DestroyWindow(m_hwndW32Window);

	// Destroy the COM Thread
	if(m_bThreadIsReady)
	{
		// Pass QUIT Message to the COM Thread
		log(LOGGING_HIGH, "Posting WM_KILLTHREAD Message to COM Thread %i",m_threadID);
		PostThreadMessage(m_threadID,WM_KILLTHREAD,NULL,NULL);

		// We need to wait to make sure COM deallocated before exiting,
		// otherwise the WPForm remains active, because deallocation never occurs
		while(m_bThreadIsReady)
			Sleep(20);

		log(LOGGING_HIGH, "Closing Thread Handle to Thread %i",m_threadID);
		CloseHandle(m_thread);

		// Also Unregister all Window Classes here
		//UnregisterClass(L"W32ButtonHookWndClass",m_hInst);		
	}
	else
	{
		log(LOGGING_LOW, "Did NOT close WPFThread m_bThreadIsReady was not true - this should not be good");
	}
}
/////////////////////////////////////////////////////////////////////
// Desc: Get the Alpha Channel / Opaqueness of the Button
/////////////////////////////////////////////////////////////////////
BYTE WPFCaller::GetAlpha()
{
	COLORREF pcrKey;
	BYTE pbAlpha;
	DWORD dwFlags;
	GetLayeredWindowAttributes(m_hwndW32Window, &pcrKey, &pbAlpha, &dwFlags);
	return pbAlpha;
}
/////////////////////////////////////////////////////////////////////
// Desc: Set the Alpha Channel / Opaqueness for the Button
/////////////////////////////////////////////////////////////////////
void WPFCaller::SetAlpha(BYTE bAlpha)
{
	SetLayeredWindowAttributes(m_hwndW32Window, RGB(255,255,255),bAlpha, LWA_COLORKEY|LWA_ALPHA);
}
/////////////////////////////////////////////////////////////////////
// Desc: Use this to start the Fade event on the Button
/////////////////////////////////////////////////////////////////////
void WPFCaller::StartRestoreEventFadeTimer()
{
	log(LOGGING_LOW,"Launching Restore Event Timer...");
	SetAlpha(0); // Hide Button Completly
	SetTimer(m_hwndW32Window, NID_RESTORE_EVENT_TIMER,20,NULL);
}
/////////////////////////////////////////////////////////////////////
// Func: OnDefault()
// Desc: Default handler for any WM_message received (ALL except WM_NCDESTROY
//		 and m_msgWMUnHook) the Destroy Messages delete this class and are handled
//		 therefore outside of it
//////////////////////////////////////////////////////////////////////
LRESULT WPFCaller::OnDefault(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam)
{
	LRESULT result = 0;
	try
	{
		// If our W32Button Cares about it, send it (no WM_USER needed now)
		if((m_hwndW32Window != NULL) && (g_IsMessageW32WindowCaresAbout(Msg)))
		{
			// work-around for Minimize/Restore. Let the parent communicate
			// to the child that it is being redrawn
			if(Msg == WM_PAINT && m_bButtonIsMinimized)
			{
				wParam = (WPARAM) m_hWnd;
				lParam = (LPARAM) m_hWnd;
				PostMessage(m_hwndW32Window,Msg,wParam,lParam);
			}
			else
			{
				SendMessage(m_hwndW32Window,Msg,wParam,lParam);
			}
		}

		// We pass the Messages we care about forward to our COM/W32/WPF Thread		
		if(m_bThreadIsReady && (m_threadID != 0))
		{
			// IF our COM object cares about this, send it
			if(g_IsMessageWPFormCaresAbout(Msg))
			{
				// We error if we use PostThreadMessage with message < WM_USER so we offset it				
				PostThreadMessage(m_threadID,(WM_USER + Msg),wParam,lParam);
			}
		}

		// Call the Default Window Proc - IMPORTANT - Always do this
		result = CallWindowProc(m_DefWndProc,hWnd,Msg,wParam,lParam);
	}
	catch(...)
	{
		log(LOGGING_LOW, "*Error* - A Fatal Error occured in WpfCaller's OnDefault()");
	}
	return result;		
}
//////////////////////////////////////////////////////////////////////
// Func: threadproc
// Desc: Thread Entry Point responsible for Dispatching the W32Window Messages
// 
// Note: We are on purpose Ignoring Error Msg 1410. If the window class is already registered it
//		 will throw an error, we can safely continuen and instantiate the window class
//////////////////////////////////////////////////////////////////////
int WINAPI WPFCaller::threadproc(HINSTANCE hinstance, HINSTANCE hPrevInstance, void* vArg, int nCmdShow)
{					
	if (!InitApplication(hinstance)) 
	{	
		// "Class already exists" this happens if the
		// same window get's called again to be hooked into.
		// this error can safely be ignored.
		if(!(GetLastError() == 1410))
		{
			log(LOGGING_LOW,"initApplication failed error %i",GetLastError());
			return FALSE; 
		}		
	}
	
	if (!InitInstance(hinstance, nCmdShow)) 
	{		
		log(LOGGING_LOW,"initInstance failed error %i",GetLastError());
		return FALSE; 
	}	

	MSG msg;		
	BOOL bGotMessage;		
	m_bThreadIsReady = true; // Thread is up N' Ready and Receiving Messages
	while ((bGotMessage = GetMessage(&msg, (HWND) NULL, 0, 0)) != 0 )
	{ 		
		if(bGotMessage == -1)
		{
			// handle the error and possible exit
			log(LOGGING_LOW,"An Error Occured in ThreadProc. Exiting!");
			ExitThread(0);
		}
		else
		{
			TranslateMessage(&msg); 
			DispatchMessage(&msg); 
		}		
	}
	
	return msg.wParam;	
}
//////////////////////////////////////////////////////////////////////
// Func: InitApplication()
// Desc: Responsible for setting up the WNDCLASSEX struct and register 
//		 the transparent window with Windows via RegisterClassEx()
//              
// Retr: TRUE, if successful, FALSE otherwise
//////////////////////////////////////////////////////////////////////
BOOL WPFCaller::InitApplication(HINSTANCE hInstance) 
{ 
	// Fill in the window class structure with parameters 
    // that describe the main window. 
	WNDCLASSEX wcx; 	
 
    wcx.cbSize = sizeof(wcx);					// size of structure
    wcx.style = CS_HREDRAW | CS_VREDRAW;        // redraw if size changes
	wcx.lpfnWndProc = (WNDPROC) W32WndProc;	    // points to W32 window procedure
    wcx.cbClsExtra = 0;							// no extra class memory
    wcx.cbWndExtra = 0;							// no extra window memory
    wcx.hInstance = hInstance;					// handle to instance
	wcx.hIcon = NULL;	
    wcx.hCursor = LoadCursor(NULL, IDC_ARROW);  // predefined arrow
    wcx.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH); // white background brush
    wcx.lpszMenuName =  L"MainMenu";			// name of menu resource
    wcx.lpszClassName = L"W32ButtonHookWndClass"; // name of window class
    wcx.hIconSm = NULL;	
 
    // Register the window class.  *may throw error 1410*
    return RegisterClassEx(&wcx); 
} 
//////////////////////////////////////////////////////////////////////
// Func: InitInstance()
// Desc: Responsible for Creating and Showing a new Window Class Instance
//              
// Retr: TRUE, if successful, FALSE otherwise
//////////////////////////////////////////////////////////////////////
BOOL WPFCaller::InitInstance(HINSTANCE hInstance, int nCmdShow) 
{   
	// Get the Dimensions of the hooked in Window
	RECT rect;
	GetWindowRect(m_hWnd,&rect);
	
	// set owner/parent window
	HWND hParent = m_hWnd;
	DWORD dwStyle;
		
	//dwStyle = ( WS_POPUP | WS_BORDER );	// For Debugging
	dwStyle = ( WS_POPUP );
	// IF parent Window get's hidden for some reason, should we hide this window?
	// by default owned windows don't hide, when their parent is hidden,
	// they do hide when parent is minimized.
	log(LOGGING_MEDIUM,"Calling CreateWindowEx with Parent %i",hParent);
	//( WS_POPUP | WS_BORDER ),  // top-level window 		
    m_hwndW32Window = CreateWindowEx( 
		( WS_EX_LAYERED ),        // A Layered window is by default transparent
        TEXT("W32ButtonHookWndClass"),   // name of window class 
        TEXT("W32ButtonHookWndTitle"),// title-bar string      
		dwStyle,					  // top-level window
		rect.left,		          // default horizontal position
        rect.top,				  // default vertical position
		(rect.right - rect.left), // default width
		(rect.bottom - rect.top), // default height
		(HWND) hParent,			  // set owner window
								  // ~An owned window is always above its owner in the Z order and
								  // is hidden when its owner is minimized
        (HMENU) NULL,			  // use class menu
         hInstance,				  // handle to application instance
        (LPVOID) NULL);           // no window-creation data	
	log(LOGGING_HIGH,"CreateWindowEx returns (m_hwndW32Window is set to %i) ",m_hwndW32Window);

	// we can use this to fade the window if we want too... (something to think about...)
	//if(SetLayeredWindowAttributes(m_hwndW32Window, RGB(255,255,255),255, LWA_COLORKEY|LWA_ALPHA)){}
	SetLayeredWindowAttributes(m_hwndW32Window, RGB(255,255,255),255, LWA_COLORKEY|LWA_ALPHA);

    // Show the window and send a WM_PAINT message to the window procedure.    
    ShowWindow(m_hwndW32Window, nCmdShow); 
    UpdateWindow(m_hwndW32Window);		

    return (m_hwndW32Window != NULL);
}